home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 050 / rsort.pqs / rsort.pas
Pascal/Delphi Source File  |  1986-01-17  |  13KB  |  366 lines

  1. program Rsort;  { (C) Copyright 1985 by Mark E Johnson  }
  2.                 {                    2272-F Benson Ave  }
  3.                 {                    St. Paul, MN 55114 }
  4.                 {                    (612)-698-3686     }
  5.  
  6.  
  7. { RSORT is a file sort program that uses an n-way merge to sort files    }
  8. { of virtually unlimited size.  It does this by reading the input file   }
  9. { in small chunks, sorting those records, and outputing them to a work   }
  10. { file.  The work file is managed by a table routine that knows where    }
  11. { the beginning and end of each 'sub-file' is.  When all the input       }
  12. { records have been read and sorted to the work file, the individual     }
  13. { sub-files are merged together in sequence and output to the destina-   }
  14. { tion file. ( If you've played Solitaire then you know how this works ) }
  15.  
  16. { An enhancement to this sort program is the idea of OWN-CODE routines.  }
  17. { You can modify the record either as it's coming into the sort, or as   }
  18. { it's being written to the output file.  Descriptions of the own-code   }
  19. { routines are detailed in the code.  The routines follow the practice   }
  20. { of NCR's SORT2 program running on an NCR 8450 in the 'N' or 'V' mode.  }
  21.  
  22. { This program has been developed in standard pascal and has been tested }
  23. { and used in Turbo Pascal.  It is meant only for sorting random record  }
  24. { files, although it is a trivial task to convert it to sort sequential  }
  25. { files of variable length.  Tests on a SLICER 80186 computer running    }
  26. { Concurrent CP/M-86 show that it will sort .5 megabytes per minute.     }
  27. { Don't expect the same performance from a IBM PC type machine.          }
  28. { This time could be decreased by using a more efficient sort, or possi- }
  29. { bly by varying the number of records per pass that it processes.       }
  30.  
  31. { NOTE: Try to optimize the performance of this program by placing the   }
  32. { TEMP work file on a separate drive.  If you have a RAM-DISK, then this }
  33. { is a good opportunity to use it.  you may place the source and destin- }
  34. { ation files on the same drive.  Remember that you must have enough     }
  35. { free space on your drives to accomodate three files of the same size   }
  36. { as your source file (unless you write over the source, then it's two)  }
  37.  
  38. { ENDDAT should be a value that you expect will NEVER appear as a data   }
  39. { item in your key field.  It MUST evaluate the same type as your key.   }
  40.  
  41. { The following equates indicate the number of records to be sorted in   }
  42. { each pass.  Generally you should allocate 16K worth of buffer.  If the }
  43. { records in the file are 256 bytes, then set passlen to 64.             }
  44. { PASSRECS must be greater than Passlen.  The maximum number of records  }
  45. { in the sort file is determined by PASSLEN * SUBFILES.  To increase or  }
  46. { decrease the maximum # of records, change SUBFILES accordingly.        }
  47.  
  48.  
  49.  const
  50.     TEMPFILE = 'temp.srt'; { name of sort work file }
  51.     ENDDAT   = 'zzzzz';
  52.     PASSLEN  = 100;        { # of records per pass }
  53.     PASSRECS = 101;        { always PASSLEN+1 }
  54.     SUBFILES = 15;         { Max number of SUBFILES to merge }
  55.  
  56.  
  57.  { The following record fields are the definition of a record in the  }
  58.  { file to be sorted.  Insert the record declaration of the file you  }
  59.  { want to sort here. Change the name of the key field to KEY_ITEM.   }
  60.  
  61. type
  62.     rectype = record
  63.         Name_First : string[20];
  64.         KEY_ITEM   : string[20];      { was name_last }
  65.         Phone      : string[8];
  66.         area_code  : string[8];
  67.     end;
  68.  
  69.  {             END OF SORT RECORD DECLARATION                         }
  70.  {          nothing needs to be changed past here                     }
  71.  
  72.     infotype = record
  73.         first          : integer;
  74.         next           : integer;
  75.         last           : integer;
  76.     end;
  77.  
  78. var
  79.     sortbuf             : array[1..PASSRECS] of rectype;
  80.  
  81.     Done,Flag           : boolean;
  82.     X,R,Hold            : integer;
  83.  
  84.     Sortndx             : integer;  { Sub-record number of Sortbuf }
  85.     Filenum             : integer;  { Current or last subfile }
  86.  
  87.     Info                : array[1..SUBFILES] of infotype;
  88.  
  89.     Totrecs             : integer;  { Total records in int file }
  90.     I,K                 : integer;
  91.     inkey,outkey        : integer; { Input and output keys }
  92.     EOFlag,quit         : boolean;
  93.     Ret_Code            : Char;    { Return code from Own Code routines }
  94.     Infile,Temp,Outfile : File of rectype;
  95.     inname,outname      : string[20];
  96.  
  97. label again, alldone;
  98.  
  99. procedure Own_Code1; { Own code routine for input records }
  100.  
  101.  { This routine is called after each record is input before     }
  102.  { sorting.  You may write code here to modify or delete  the   }
  103.  { record before sorting.  One common use of this routine is to }
  104.  { compare the record for a type of field which you do not want }
  105.  { in the sorted file.  For example, if we are sorting a mailing }
  106.  { list, we may not want any names from outside the USA.        }
  107.  { We could check the ZIP code and pass only those ZIPs that    }
  108.  { indicate an address inside Continental USA.                  }
  109.  
  110.  { CALLED FROM: MAIN                                            }
  111.  { PARAMETERS : current record is in SORTBUF[SORTNDX]           }
  112.  
  113.  { Returns "RET_CODE" Which may be one of the following values: }
  114.  {          D - Delete this record (Throw it away)              }
  115.  {          K - Keep this record                                }
  116.  
  117. begin
  118.     Ret_Code:='K';
  119. End;
  120.  
  121. procedure Own_Code2;  { Own code routine for output records }
  122.  
  123.  { This routine is called before a sorted record is output to }
  124.  { the destination file.  One common use of this routine may  }
  125.  { be to eleminate any duplicate records, or convert ASCII to }
  126.  { EBCDIC, upper to lower case, or more involved operations   }
  127.  { such as filling in certain fields based on calculated      }
  128.  { results from other fields. }
  129.  
  130.  { CALLED FROM: OUTPUT                                               }
  131.  { PARAMETERS : current record is in SORTBUF[HOLD]                   }
  132.  { RETURNS    : "RET_CODE" Which may be one of the following values: }
  133.  {              D - Delete this record (Throw it away)               }
  134.  {              K - Keep this record                                 }
  135.  
  136. begin
  137.     Ret_Code:='K';    { for now, Always keep current record }
  138. End;
  139.  
  140. procedure init;
  141. begin
  142.     writeln('Enter input file name ');
  143.     readln(inname);
  144.     writeln('Enter output file name ');
  145.     readln(outname);
  146. end;
  147.  
  148. procedure Getinp;
  149.  
  150. { this procedure reads a record from the input file and }
  151. { stores it in SORTBUF[SORTNDX]                         }
  152.  
  153. { CALLED FROM: MAIN                                     }
  154. { RETURNS    : new record is in SORTBUF[SORTNDX]        }
  155.  
  156. begin
  157.     if eof(infile) then
  158.         EOFLAG:=true
  159.     else
  160.         begin
  161.         seek(infile,inkey);
  162.         read(infile,sortbuf[sortndx]);
  163.         inkey:=inkey+1;
  164.     end;
  165.  
  166. End;
  167.  
  168. procedure Puttemp; { Write record to temp file }
  169. { This procedure writes the record in SORTBUF[I] to the work file }
  170.  
  171. { CALLED FROM: MAIN                                               }
  172. { PARAMETERS : current record is in SORTBUF[I]                    }
  173.  
  174. begin
  175.     seek(temp,k);
  176.     write(temp,sortbuf[i]);
  177.     K:=K+1;
  178.  
  179. End;
  180.  
  181. procedure Output;
  182. { This procedure writes a record to the destination file }
  183.  
  184. { CALLED FROM: MERGE                                     }
  185. { PARAMETERS : current record is in SORTBUF[HOLD]        }
  186. { CALLS      : OWN_CODE2                                 }
  187.  
  188. begin
  189.     Own_Code2;
  190.     If Ret_code = 'K' Then
  191.         begin
  192.         seek(outfile,outkey);
  193.         write(outfile,sortbuf[hold]);
  194.         outkey:=outkey+1;
  195.     end;
  196.     if info[hold].next <= info[hold].last then
  197.         begin
  198.         seek(temp,info[hold].next);
  199.         read(temp,sortbuf[hold]);
  200.         info[hold].next:=info[hold].next+1;
  201.         if eof(temp) then
  202.             sortbuf[hold].KEY_ITEM:=ENDDAT;
  203.     End
  204.     Else
  205.         sortbuf[hold].KEY_ITEM:=ENDDAT;
  206.     r:=hold+1;
  207. End;
  208.  
  209. procedure Sort;  { Bubble sort }
  210.  { this routine sorts the record array SORTBUF[1..SORTNDX] in ascending }
  211.  { order, using KEY_ITEM as the sort key                                }
  212.  
  213.  { CALLED FROM: MAIN                                                    }
  214.  { PARAMETERS : SORTBUF[1..SORTNDX]                                     }
  215.  
  216.  
  217. var
  218.     C : rectype;   { hold area for swapping }
  219.     I : integer;
  220.     re_iter : boolean;
  221.  
  222. begin
  223.     re_iter:=TRUE;
  224.     while re_iter=TRUE
  225.         begin
  226.         re_iter:=FALSE;
  227.         for i:=1 to sortndx-1 do
  228.             begin
  229.             If Sortbuf[i].KEY_ITEM > Sortbuf[i+1].KEY_ITEM Then
  230.                 begin
  231.                 C:=sortbuf[i];
  232.                 sortbuf[i]:=sortbuf[i+1];
  233.                 sortbuf[i+1]:=c;
  234.                 re_iter:=TRUE;
  235.             end;
  236.         end;
  237.     end;
  238. End;
  239.  
  240. procedure Merge;
  241. { This procedure merges the subfiles in the workfile, and creates }
  242. { the destination file.                                           }
  243.  
  244. { CALLED FROM: MAIN                                               }
  245. { PARAMETERS : INFO[1..FILENUM] contains the start and end record }
  246. {              for each subfile in file TEMP.                     }
  247. { CALLS      : OUTPUT                                             }
  248.  
  249. var
  250.     J : integer;
  251.     i : integer;
  252. begin
  253.     assign(temp,TEMPFILE);
  254.     reset(temp);
  255.     for i:=1 to filenum do
  256.         begin
  257.         If info[i].First >= 0 Then
  258.             begin
  259.             seek(temp,info[i].first);
  260.             read(temp,sortbuf[i]);
  261.             info[i].Next:=info[i].First+1;
  262.             End;
  263.     end;
  264.     writeln('Performing Merge');
  265.     Done:=FALSE;
  266.     while done=FALSE Do
  267.         begin
  268.         r:=1;
  269.         hold:=r;
  270.         if r=hold Then r:=r+1;
  271.         if r > PASSLEN then r:=1;
  272.         if r=hold then
  273.             writeln('Internal error, R=HOLD = ',hold);
  274.         for i:=1 to PASSRECS-1 do   { Filenum-1 }
  275.             begin
  276.             if sortbuf[hold].KEY_ITEM <= sortbuf[r].KEY_ITEM Then
  277.                 begin
  278.                 Flag:=TRUE;
  279.                 r:=r+1;
  280.             end
  281.             Else
  282.                 begin
  283.                 flag:=FALSE;
  284.                 hold:=r;
  285.                 r:=r+1;
  286.             end;
  287.             if r > filenum then
  288.                 r:=1;
  289.         end;
  290.         if flag=TRUE then
  291.             output;
  292.         done:=TRUE;
  293.         for j:=1 to filenum do
  294.             begin
  295.             if sortbuf[j].KEY_ITEM < ENDDAT then
  296.                 Done:=FALSE;
  297.         end;
  298.     End;
  299.     Close(Outfile);
  300.     Close(Temp);
  301.  
  302. End;
  303.  
  304. begin { MAIN }
  305. { This is the main program.  It starts by building the TEMP file, then }
  306. { calling the procedure MERGE.                                         }
  307.  
  308. { CALLS : INIT, GETINP, OWN_CODE1, SORT, MERGE                         }
  309.  
  310.     init;
  311.     assign(infile,inname);
  312.     reset(infile);
  313.     assign(temp,TEMPFILE);
  314.     rewrite(temp);
  315.     assign(outfile,outname);
  316.     rewrite(outfile);
  317.  
  318.     EOFlag:=FALSE;
  319.     Quit:=FALSE;
  320.     Sortndx:=1;
  321.     Filenum:=1;
  322.     Info[1].First:=0;
  323.     inkey:=0;  { Starting key for input file  }
  324.     outkey:=0; { Starting key for output file }
  325.     K:=0;      { Starting key for Temp  file  }
  326.  
  327.     totrecs:=1;
  328.     while quit = FALSE do
  329.         begin
  330.  Again:
  331.         Getinp;       { get a record }
  332.         Own_code1;
  333.         if ret_code='D' Then
  334.             Goto Again;
  335.         Sortndx:=Sortndx+1;
  336.         If (Sortndx > PASSLEN) or (EOFlag=TRUE) Then   { Buffer overflow }
  337.             begin
  338.             Sortndx:=Sortndx-1;
  339.             If EOFlag=TRUE Then
  340.                 begin
  341.                 Totrecs:=Totrecs-1;
  342.                 quit:=TRUE;
  343.             end;
  344.             Sort;   { Sort buffer }
  345.             if sortndx = 0 then
  346.                 goto alldone;
  347.             writeln('Writing Subfile ',Filenum);
  348.             for I:=1 to Sortndx do         { Write to temp file      }
  349.                 Puttemp;
  350.             Info[Filenum].Last:=Totrecs-1; { Save last record number }
  351.             Filenum:=Filenum+1;            { Start new subfile       }
  352.             Info[Filenum].First:=Totrecs;  { Save starting record    }
  353.             Sortndx:=1;                    { Reset sort buffer index }
  354.         end;
  355.         totrecs:=totrecs+1;
  356.     end;
  357.  alldone:
  358.     info[filenum].last:=totrecs;
  359.     filenum:=filenum-1;
  360.     writeln('Total records input = ',totrecs-1);
  361.     Close(Infile);
  362.     Close(Temp);
  363.     Merge;
  364.     writeln('Total records merged: ',outkey);
  365.  End.
  366.